package com.zwh.se.collection.map;



import com.zwh.tool.utils.MapUtils;

import java.util.*;

/**
 * @author yexi
 * <p>
 * Map,翻译为映射,说映射表示两个集合之间各自元素的一种"对应"的关系,数学中记作 f：A→B 。
 * Map并不是集合，而表示两个集合之间的一种关系，故Map没有实现 Collection 接口(不过鉴于其与集合的关系,还是放在这里)
 * A集合中的元素称之为 key,把B集合中的元素称之为 value,被称为"键值对"
 * A集合是一个Set集合，B集合是Collection集合。
 * <p>
 * HashMap底层基于哈希算法实现的。所谓哈希算法,就是通过算出存储的key对象的hashCode值后,通过某种表达式来决定其在哈希表中存储位置的一种算法。
 * 因此HashMap的key也不存在索引的概念,无序(用底层实现上来说,HashMap的key就是HashSet)且不允许重复。
 * 用包装类作为key时,由于其hashCode和equals已经重写,所以可以随意地使用;如果你的key是自定义对象时,一定要重写hashCode和equals方法。
 * 实际开发中，一般而言，我们经常是用String作为key。
 * <p>
 * HashMap是基于哈希表的 Map 接口的实现。
 * 此实现提供所有可选的映射操作，并允许使用 null 值和 null 键。
 * （除了非同步和允许使用 null 之外，HashMap 类与 Hashtable 大致相同。）
 * 此类不保证映射的顺序，特别是它不保证该顺序恒久不变。
 * TIPS:简单来说:HashMap就是不同步且允许null的HashTable
 * TIPS:由于map的key本质就是set,所以key关于对象的存储规则上与set相同,详情前往查看{@link UseHashSetWhenUseObject}
 */
public class HashMapDemo {
    public static void main(String[] args) {

        HashMap<String, String> map = new HashMap<>();


        System.out.println("--------------------[1]hashMap的添加操作--------------------");
        //hashMap允许存入nulld的key值或value值
        map.put(null, "空值");
        map.put("A", "Apple");
        map.put("B", "banana");
        map.put("C", "coco");
        System.out.println(map.size());

        System.out.println("--------------------[2]hashMap的删除操作--------------------");
        map.remove("A");
        System.out.println(map.size());

        System.out.println("--------------------[3]hashMap的修改操作--------------------");
        //key不可重复,当put的时候key重复了就用新value替换掉旧value
        System.out.println(map.put("C", "COCO"));//如果put的时候将某个value替换掉的话,则返回value值,否则返回null
        System.out.println(map);
        System.out.println(map.put(null, null));//如果集合中存在null的key,也一样会被替换掉

        System.out.println("--------------------[4]hashMap的查询操作--------------------");
        String val = map.get("C");
        System.out.println(val);
        //boolean containsKey(Object key):如果此映射包含对于指定键的映射关系，则返回 true。
        System.out.println(map.containsKey("A"));
        //boolean containsValue(Object value):如果此映射将一个或多个键映射到指定值，则返回 true。
        System.out.println(map.containsValue("Apple"));


        System.out.println("--------------------[5]hashMap的遍历操作--------------------");

        //Set<K> keySet():返回此映射中所包含的键的 Set 视图。
        //      简单来说就是直接返回key的set集合
        Set<String> keySet = map.keySet();
        Iterator<String> iterator = keySet.iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            String value = map.get(key);
            System.out.println(key + "==>" + value);
        }

        //Collection<V> values():返回此映射所包含的值的 Collection 视图。
        //      简单来说就是直接获取所有的value(这种方法没法获取key)
        Collection<String> values = map.values();
        Iterator<String> it2 = values.iterator();
        while (it2.hasNext()) {
            String value = it2.next();
            System.out.println(value);
        }

        //Set<Map.Entry<K,V>> entrySet():返回此映射所包含的映射关系的 Set 视图。
        //   简单来说就是直接返回Entry的set集合
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        Iterator<Map.Entry<String, String>> it3 = entrySet.iterator();
        while (it3.hasNext()) {
            Map.Entry<String, String> entry = it3.next();
            System.out.println(entry.getKey() + "==>" + entry.getValue());
        }

        System.out.println("--------------------[6]hashMap的综合操作介绍--------------------");
        //boolean isEmpty():如果此映射不包含key-value，则返回true 。
        System.out.println(map.isEmpty());
        //int size():返回此映射中的键-值映射关系数(也就是存放的元素个数,注意这个不等于数组的长度)。
        System.out.println(map.size());
        //boolean equals(Object o):比较双方的映射是否相等。更准确来说,应该是m1.entrySet().equals(m2.entrySet()),则两个映射m1和m2表示相同的映射。
        HashMap<String, String> newMap = new HashMap<>(map);
        System.out.println(newMap == map);
        System.out.println(map.equals(newMap));
        //void clear():从此映射中移除所有映射关系。
        map.clear();

        System.out.println("--------------------[0]hashMap的基本特性--------------------");
        System.out.println("----不声明容量初始化----");
        map = new HashMap<>();
        System.out.println(MapUtils.getHashMapCapacity(map));//容量,16
        System.out.println(MapUtils.getHashMapThreshold(map));//阈值,0,直接初始化不赋值就是0
        System.out.println("----声明容量初始化----");
        map = new HashMap<>(1);
        System.out.println(MapUtils.getHashMapCapacity(map));//容量,1
        System.out.println(MapUtils.getHashMapThreshold(map));//阈值,1,直接初始化且赋值时阈值等同于容量,直至加入元素后重新计算
        map = new HashMap<>(10);
        System.out.println(MapUtils.getHashMapCapacity(map));//容量,16,可见从某个度开始,容量开始使用一些固定值初始化(2^n)
        System.out.println(MapUtils.getHashMapThreshold(map));//阈值,16
        System.out.println(MapUtils.tableSizeFor(10));//观看容量初始化的操作
        System.out.println("----观察扩容过程变化----");
        map = new HashMap<>(1);
        Integer cas = 1;
        for (int i = 0; i < 16; i++) {
            map.put(i + "test", "1");
            Object capacity = MapUtils.getHashMapCapacity(map);
            if (!cas.equals(capacity)) {
                System.out.println("如今是第" + (i + 1) + "次put");
                System.out.println("现在map的容量是:" + capacity);
                System.out.println("现在map的扩容阈值是:" + MapUtils.getHashMapThreshold(map) + '\n');
                cas = Integer.valueOf(capacity.toString());
            }
        }
        //可以清晰看到每当put之后的因子数量超过阈值之时，就会扩容(容量变为原来的2倍,为了2^n,减少碰撞)
        System.out.println("----初始化时可指定负载因子----");
        map = new HashMap<>(16, 0.5f);
        map.put("test", "1");
        System.out.println(MapUtils.getHashMapCapacity(map));//容量,16
        System.out.println(MapUtils.getHashMapThreshold(map));//阈值,8
    }

}
